#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include "x11_simulate_event.h"

#define logd printf
#define loge printf
#define T3 logd

static Window s_self=0, s_parent=0;
int enum_windows(Display * dpy, Window self, int level)
{
    Window out_root, out_parent, *out_children;
    unsigned int out_num;
    if (XQueryTree(dpy, self, &out_root, &out_parent, &out_children, &out_num) == 0) {
        logd("query failed.\n");
        exit(-1);
    }

    XClassHint * hint = XAllocClassHint();
    XGetClassHint(dpy, self, hint);
    
    logd("window:0x%08x, out_parent:0x%08x, children:%d, res_name:%s, res_class:%s, level:%d\n", 
        (int)self, (int)out_parent, out_num, hint->res_name, hint->res_class,
        level);

    if ((level == 2) && (hint->res_name) && (strcmp(hint->res_name, "test_shell") == 0)) {
        logd("xxxxxxxxxxxxxxx found it: 0x%08x xxxxxxxxxxxxxxxxxxx\n", (int)self);
    }
    XFree(hint);

    if (level < 2) {
        for (int i=0; i<out_num; i++) {
            enum_windows(dpy, out_children[i], level+1);
        }
    }

    XFree(out_children);
    return 0;
}

static void destroy_event( GtkWidget *widget,
        gpointer   data )
{
    gtk_main_quit ();
}

static gboolean delete_event( GtkWidget *widget,
        GdkEvent  *event,
        gpointer   data )
{
    return FALSE;
}

static gboolean buttonpress_event( GtkWidget *widget,
        GdkEventButton  *e,
        gpointer   data )
{
    logd("button %d, state:%d\n", e->button, e->state);
    return TRUE;
}

static gboolean keypress_event( GtkWidget *widget,
        GdkEventKey  *e,
        gpointer   data )
{
    int r;
    static char c = 'a';
    GdkKeymapKey *keys = NULL;
    gboolean success;
    gint n_keys = 0;
    Display * dpy = XOpenDisplay(":0.0");

    logd("key:0x%08x, state:%d, '%c', '%d'\n", e->keyval, e->state, e->keyval, e->keyval);
    if (e->keyval == 0x0000ff1b) {
        gtk_main_quit();
    }
    else if (e->keyval == 0x0000ff0d) {
        r = _gdk_x11_window_simulate_key (dpy, s_self, 0, 0, c++, 0, GDK_KEY_PRESS);
        logd("simkey: r=%d\n", r);
    }
    else {
    }

    return TRUE;
}

int test_enum(int argc, char ** argv)
{
    Display * dpy = XOpenDisplay(":0.0");
    Window root = DefaultRootWindow(dpy);
    logd("dpy=%p, root=0x%08x\n", dpy, (int)root);
    enum_windows(dpy, root, 0);
    return 0;
}

int test_reparent(int argc, char ** argv)
{
    Display * dpy = XOpenDisplay(":0.0");
    Window root = DefaultRootWindow(dpy);
    logd("dpy=%p, root=0x%08x\n", dpy, (int)root);
 
    gtk_set_locale ();
    gtk_init (&argc, &argv);

    GtkWidget * window;
    if (argc > 1) {
        Window re_parent = strtoll(argv[1], NULL, 16);
        logd("re parent to:0x%08x, %d\n", (int)re_parent, (int)re_parent);

        //window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
        window = gtk_window_new (GTK_WINDOW_POPUP);
        gtk_container_set_border_width (GTK_CONTAINER (window), 0);
        GdkColor cl = {0, 0x0, 0x0, 0xffff};
        gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &cl);
        gtk_window_resize(GTK_WINDOW(window), 200, 300);
        g_signal_connect (window, "key-press-event", G_CALLBACK (keypress_event), NULL);
        gtk_widget_show_now (window);
        s_self = GDK_WINDOW_XID(window->window);
        s_parent = re_parent;
        logd("window: s:%08x, p:%08x\n",  (int)s_self, (int)s_parent);
        XReparentWindow(dpy, s_self,  s_parent, 50, 50);
        XFlush(dpy);
        XSync(dpy, False);
    }
    else {
        //window = gtk_window_new (GTK_WINDOW_POPUP);
        window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
        gtk_window_set_title(GTK_WINDOW(window), "休息提醒小程序");
        gtk_window_resize(GTK_WINDOW(window), 500, 400);
        //gtk_window_set_keep_above(GTK_WINDOW(window), TRUE); 
        gtk_container_set_border_width (GTK_CONTAINER (window), 10);
        gtk_widget_show_now (window);

        s_self = GDK_WINDOW_XID(window->window);
        gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
        // gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK);
        // gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
        g_signal_connect (window, "delete-event", G_CALLBACK (delete_event), NULL);
        g_signal_connect (window, "destroy", G_CALLBACK (destroy_event), NULL);
        g_signal_connect (window, "key-press-event", G_CALLBACK (keypress_event), NULL);
        g_signal_connect (window, "button-press-event", G_CALLBACK (buttonpress_event), NULL);
        g_signal_connect (window, "button-release-event", G_CALLBACK (buttonpress_event), NULL);

        XSync(dpy, False);
        logd("window: %d, 0x%08x\n", (int)GDK_WINDOW_XID(window->window), (int)GDK_WINDOW_XID(window->window));
    }

    //XSync(dpy, False);

    gtk_main ();

    return 0;
}

int x11_set_focus_window(Display * dpy, Window root, Window win)
{
    XEvent xev; 
    Atom atom_active;

    atom_active = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
    xev.xclient.type = ClientMessage;
    xev.xclient.serial = 0;  
    xev.xclient.send_event = True;
    xev.xclient.window = win; 
    xev.xclient.message_type = atom_active;
    xev.xclient.format = 32; 
    xev.xclient.data.l[0] = 2;  
    xev.xclient.data.l[1] = CurrentTime;
    xev.xclient.data.l[2] = win; 
    xev.xclient.data.l[3] = 0;  
    xev.xclient.data.l[4] = 0;  

    XSendEvent(dpy, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
    return 0;
}

Window x11_get_focus_window(Display * dpy, Window root)
{
    Atom a = XInternAtom(dpy, "_NET_ACTIVE_WINDOW" , True);
    Atom actualType;
    int format;
    unsigned long numItems, bytesAfter;
    unsigned char *data =0;
    int status = XGetWindowProperty (dpy, root, a,
            0L,
            (~0L),
            False,
            AnyPropertyType,
            &actualType,
            &format,
            &numItems,
            &bytesAfter,
            &data);

    if (status >= Success && numItems) {
        Window r = ((Window *) data)[0];
        XFree(data);
        return r;
    }    
    else {
        loge("get focused window failed\n");
    }    
    return 0;
}


int main(int argc, char **argv)
{
#if 0
    Display * dpy = XOpenDisplay(":0.0");
    Window parent = atol(argv[1]);
    Window win = strtol(argv[2], 0, 16);
    logd("w:%d, p:%d\n", (int)win, (int)parent);
    int r = XReparentWindow(dpy, win,  parent, 50, 50);
    logd("r = %d\n", r);
    XSync(dpy, 0);
#endif
    test_enum(argc, argv);
    // test_reparent(argc, argv);

    Display * dpy = XOpenDisplay(":0.0");
    Window root = DefaultRootWindow(dpy);
#if 0
    Window w = (Window)strtol(argv[1], NULL, 16);
    printf("w=%x\n", (int)w);
    x11_set_focus_window(dpy, root, w);
    XSync(dpy, 0);
    sleep(1);
#endif 
    Window w;
    for (int i=0; ; i++) {
        w = x11_get_focus_window(dpy, root);
        printf("w=%x\n", w);
        sleep(1);
    }
    return 0;
}


